package com.db4o.odbgen.plugins.java.maingenerator.typedxmlschema;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;

import com.db4o.internal.odbgen.TemplatesUtils;
import com.db4o.internal.odbgen.plugins.TemplateList;
import com.db4o.odbgen.OdbgenException;
import com.db4o.odbgen.plugins.java.maingenerator.MainGenerator;


/**
 * 
 * Handles writing of source files to disk given {@link TypedXmlSchema}.
 * @author liviug
 */
public class CodeStorage {
    
    private TypeDeclarationCollection _sourceFiles;
    /**
     * The base directory of all source filed.
     * Each package will be relative to this.
     */
    private File _baseDir;
    private String _standardTemplatesJar;

    
    
    /**
     * 
     * @author liviug
     * Contains the list of standard templates used by {@link CodeStorage}.
     */
    private enum Templates implements TemplateList{
        CLASS(Templates.path, "class.vm"),
        INTERFACE(Templates.path, "interface.vm"),
        ENUM(Templates.path, "enum.vm");
        private static final String path = "standardPlugins/java/maingenerator";

        
        private String _path;
        private String _name;

        Templates(String path, String name){
            this._path = path;
            this._name = name;
        }
        
        /**
         * Returns the path and the name of the template.
         */
        public String getQualifiedName() {
            return this._path+"/"+this._name;
        }
    }

    public CodeStorage(TypedXmlSchema typedXmlSchema, File baseDir, String standardTemplatesJar){
        this._baseDir = baseDir;
        this._standardTemplatesJar = standardTemplatesJar;
        
        this._sourceFiles = typedXmlSchema.getTypeDeclarations();
    }
    
    /**
     * Writes the the generated code to disk.
     * @param basePath The root path of all generated code.
     * @throws OdbgenException 
     */
    public void writeStorage() throws OdbgenException{

        for(TypeDeclaration sf : this._sourceFiles){
            if (sf instanceof TSClass) {
                String content = CodeStorage.generateClassContents(this._standardTemplatesJar, (TSClass)sf, false);
                this.writeSourceFile(sf, content);
            }
            if (sf instanceof TSInterface) {
                String content = this.generateInterface((TSInterface)sf);
                this.writeSourceFile(sf, content);
            }
            if (sf instanceof TSEnum) {
                String content = this.generateEnum((TSEnum)sf);
                this.writeSourceFile(sf, content);
            }
        }
    }
    
    /**
     * Returns the contents of the given class.
     * @param standardTemplatesJar The jar that contains the standard templates. This can be
     * obtained from {@link MainGenerator}.
     * @param tsClass The class to generate the contents for.
     * @param excludePackage If true, the package will not be included in contents. This is
     * usefull, for example, when generating the contents for an inner class.
     * @return
     * @throws OdbgenException
     */
	public static String generateClassContents(String standardTemplatesJar, TSClass tsClass, boolean excludePackage) throws OdbgenException {
		Template template = TemplatesUtils.getTemplateJar(CodeStorage.Templates.CLASS.getQualifiedName(), standardTemplatesJar);
        VelocityContext context = new VelocityContext();
        context.put("class", tsClass );
        context.put("excludePackage", excludePackage );
        String res = TemplatesUtils.merge(template, context);
        return res;
	}
    

    /**
     * Generates the content of the specified interface. 
     * @param tsInterface The interface that must be generated.
     * @throws OdbgenException
     */
    private String generateInterface(TSInterface tsInterface) throws OdbgenException {
        Template template = TemplatesUtils.getTemplateJar(CodeStorage.Templates.INTERFACE.getQualifiedName(), this._standardTemplatesJar);
        VelocityContext context = new VelocityContext();
        context.put("interface", tsInterface);
        String res = TemplatesUtils.merge(template, context);
        return res;
    }

    /**
     * Generates the content of the specified enum. 
     * @param tsEnum The enum that must be generated.
     * @throws OdbgenException
     */
    private String generateEnum(TSEnum tsEnum) throws OdbgenException {
        Template template = TemplatesUtils.getTemplateJar(CodeStorage.Templates.ENUM.getQualifiedName(), this._standardTemplatesJar);
        VelocityContext context = new VelocityContext();
        context.put("enum", tsEnum );
        String res = TemplatesUtils.merge(template, context);
        return res;
    }

    /**
     * Returns the list of source files.
     * Use this to add additional source files.
     * @return
     */
    public TypeDeclarationCollection getSourceFiles() {
        return _sourceFiles;
    }
    
    /**
     * Writes the specified source file to disk.
     * @param csSourceFile Details about the file to be written. This includes the package
     * and the file name.
     * @param content The content of the file. 
     * @throws OdbgenException
     */
    private void writeSourceFile(TypeDeclaration csSourceFile, String content) throws OdbgenException {
        
        //Get the directory path to write the file into. This is based on file's package.
        StringBuilder packagePath = new StringBuilder();
        packagePath.append(this._baseDir.getAbsolutePath());
        packagePath.append('/');
        packagePath.append(csSourceFile.getPackage().replaceAll("\\.", "/"));
        
        File packageDir= new File(packagePath.toString());
        if(!packageDir.exists()){
            if(!packageDir.mkdirs()){
                throw new OdbgenException("Could not create directory '%s'.", packageDir.getAbsolutePath());
            }
        }

        //Write the file.
        File sourceFile = new File(packageDir.getAbsolutePath()+"/"+csSourceFile.getSimpleName()+".java");
        FileWriter fw = null;
        try{
            fw = new FileWriter(sourceFile);
            try{
                fw.write(content.toCharArray());
            }
            finally{
                fw.close();
            }
        }
        catch(IOException e){
            throw new OdbgenException(
                    e, 
                    "Could not write source file '%s'.", 
                    sourceFile.getAbsolutePath()
                    );
        }
    }
    
    
}



